{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Copyright (c) MONAI Consortium  \n",
    "Licensed under the Apache License, Version 2.0 (the \"License\");  \n",
    "you may not use this file except in compliance with the License.  \n",
    "You may obtain a copy of the License at  \n",
    "&nbsp;&nbsp;&nbsp;&nbsp;http://www.apache.org/licenses/LICENSE-2.0  \n",
    "Unless required by applicable law or agreed to in writing, software  \n",
    "distributed under the License is distributed on an \"AS IS\" BASIS,  \n",
    "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  \n",
    "See the License for the specific language governing permissions and  \n",
    "limitations under the License.\n",
    "\n",
    "# MONAI 101 tutorial\n",
    "\n",
    "In this tutorial, we will introduce how simple it can be to run an end-to-end classification pipeline with MONAI.\n",
    "\n",
    "These steps will be included in this tutorial, and each of them will take only a few lines of code:\n",
    "- Dataset download\n",
    "- Data pre-processing\n",
    "- Define a DenseNet-121 and run training\n",
    "- Check the results on test dataset\n",
    "\n",
    "This tutorial will use about 7GB of GPU memory and 10 minutes to run.\n",
    "\n",
    "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Project-MONAI/tutorials/blob/main/2d_classification/monai_101.ipynb)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup environment"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!python -c \"import monai\" || pip install -q \"monai-weekly[ignite, tqdm]\""
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup imports"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/local/lib/python3.8/dist-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "MONAI version: 1.2.0rc4+21.g7f067564\n",
      "Numpy version: 1.22.2\n",
      "Pytorch version: 2.0.0a0+1767026\n",
      "MONAI flags: HAS_EXT = False, USE_COMPILED = False, USE_META_DICT = False\n",
      "MONAI rev id: 7f06756472fd5514c3c2f2a6710e3fa4d1748e90\n",
      "MONAI __file__: /workspace/monai/monai-in-dev/monai/__init__.py\n",
      "\n",
      "Optional dependencies:\n",
      "Pytorch Ignite version: 0.4.11\n",
      "ITK version: 5.3.0\n",
      "Nibabel version: 5.1.0\n",
      "scikit-image version: 0.20.0\n",
      "Pillow version: 9.2.0\n",
      "Tensorboard version: 2.9.0\n",
      "gdown version: 4.7.1\n",
      "TorchVision version: 0.15.0a0\n",
      "tqdm version: 4.65.0\n",
      "lmdb version: 1.4.1\n",
      "psutil version: 5.9.4\n",
      "pandas version: 1.5.2\n",
      "einops version: 0.6.1\n",
      "transformers version: 4.21.3\n",
      "mlflow version: 2.3.0\n",
      "pynrrd version: 1.0.0\n",
      "\n",
      "For details about installing the optional dependencies, please visit:\n",
      "    https://docs.monai.io/en/latest/installation.html#installing-the-recommended-dependencies\n",
      "\n"
     ]
    }
   ],
   "source": [
    "import logging\n",
    "import numpy as np\n",
    "import os\n",
    "from pathlib import Path\n",
    "import sys\n",
    "import tempfile\n",
    "import torch\n",
    "\n",
    "from monai.apps import MedNISTDataset\n",
    "from monai.config import print_config\n",
    "from monai.data import DataLoader\n",
    "from monai.engines import SupervisedTrainer\n",
    "from monai.handlers import StatsHandler\n",
    "from monai.inferers import SimpleInferer\n",
    "from monai.networks import eval_mode\n",
    "from monai.networks.nets import densenet121\n",
    "from monai.transforms import LoadImageD, EnsureChannelFirstD, ScaleIntensityD, Compose\n",
    "\n",
    "print_config()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup data directory\n",
    "\n",
    "You can specify a directory with the `MONAI_DATA_DIRECTORY` environment variable.  \n",
    "This allows you to save results and reuse downloads.  \n",
    "If not specified a temporary directory will be used."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "/workspace/data\n"
     ]
    }
   ],
   "source": [
    "directory = os.environ.get(\"MONAI_DATA_DIRECTORY\")\n",
    "if directory is not None:\n",
    "    os.makedirs(directory, exist_ok=True)\n",
    "root_dir = tempfile.mkdtemp() if directory is None else directory\n",
    "print(root_dir)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Use MONAI transforms to preprocess data\n",
    "\n",
    "Medical images require specialized methods for I/O, preprocessing, and augmentation.\n",
    "They often follow specific formats, are handled with specific protocols, and the data arrays are often high-dimensional.\n",
    "\n",
    "In this example, we will perform image loading, data format verification, and intensity scaling with three `monai.transforms` listed below, and compose a pipeline ready to be used in next steps."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "transform = Compose(\n",
    "    [\n",
    "        LoadImageD(keys=\"image\", image_only=True),\n",
    "        EnsureChannelFirstD(keys=\"image\"),\n",
    "        ScaleIntensityD(keys=\"image\"),\n",
    "    ]\n",
    ")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Prepare datasets using MONAI Apps\n",
    "\n",
    "We use `MedNISTDataset` in MONAI Apps to download a dataset to the specified directory and perform the pre-processing steps in the `monai.transforms` compose.\n",
    "\n",
    "The MedNIST dataset was gathered from several sets from [TCIA](https://wiki.cancerimagingarchive.net/display/Public/Data+Usage+Policies+and+Restrictions),\n",
    "[the RSNA Bone Age Challenge](http://rsnachallenges.cloudapp.net/competitions/4),\n",
    "and [the NIH Chest X-ray dataset](https://cloud.google.com/healthcare/docs/resources/public-datasets/nih-chest).\n",
    "\n",
    "The dataset is kindly made available by [Dr. Bradley J. Erickson M.D., Ph.D.](https://www.mayo.edu/research/labs/radiology-informatics/overview) (Department of Radiology, Mayo Clinic)\n",
    "under the Creative Commons [CC BY-SA 4.0 license](https://creativecommons.org/licenses/by-sa/4.0/).\n",
    "\n",
    "If you use the MedNIST dataset, please acknowledge the source. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2023-04-21 15:37:46,567 - INFO - Verified 'MedNIST.tar.gz', md5: 0bc7306e7427e00ad1c5526a6677552d.\n",
      "2023-04-21 15:37:46,567 - INFO - File exists: /workspace/data/MedNIST.tar.gz, skipped downloading.\n",
      "2023-04-21 15:37:46,568 - INFO - Non-empty folder exists in /workspace/data/MedNIST, skipped extracting.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Loading dataset: 100%|██████████| 47164/47164 [00:18<00:00, 2525.58it/s]\n"
     ]
    }
   ],
   "source": [
    "dataset = MedNISTDataset(root_dir=root_dir, transform=transform, section=\"training\", download=True)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Define a network and a supervised trainer\n",
    "\n",
    "To train a model that can perform the classification task, we will use the DenseNet-121 which is known for its performance on the ImageNet dataset.\n",
    "\n",
    "For a typical supervised training workflow, MONAI provides `SupervisedTrainer` to define the hyper-parameters."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "max_epochs = 5\n",
    "model = densenet121(spatial_dims=2, in_channels=1, out_channels=6).to(\"cuda:0\")\n",
    "\n",
    "logging.basicConfig(stream=sys.stdout, level=logging.INFO)\n",
    "trainer = SupervisedTrainer(\n",
    "    device=torch.device(\"cuda:0\"),\n",
    "    max_epochs=max_epochs,\n",
    "    train_data_loader=DataLoader(dataset, batch_size=512, shuffle=True, num_workers=4),\n",
    "    network=model,\n",
    "    optimizer=torch.optim.Adam(model.parameters(), lr=1e-5),\n",
    "    loss_function=torch.nn.CrossEntropyLoss(),\n",
    "    inferer=SimpleInferer(),\n",
    "    train_handlers=StatsHandler(),\n",
    ")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Run the training"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "trainer.run()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Check the prediction on the test dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Class prediction is AbdomenCT. Ground-truth: AbdomenCT\n",
      "Class prediction is BreastMRI. Ground-truth: BreastMRI\n",
      "Class prediction is ChestCT. Ground-truth: ChestCT\n",
      "Class prediction is CXR. Ground-truth: CXR\n",
      "Class prediction is Hand. Ground-truth: Hand\n",
      "Class prediction is HeadCT. Ground-truth: HeadCT\n",
      "Class prediction is HeadCT. Ground-truth: HeadCT\n",
      "Class prediction is CXR. Ground-truth: CXR\n",
      "Class prediction is ChestCT. Ground-truth: ChestCT\n",
      "Class prediction is BreastMRI. Ground-truth: BreastMRI\n"
     ]
    }
   ],
   "source": [
    "dataset_dir = Path(root_dir, \"MedNIST\")\n",
    "class_names = sorted(f\"{x.name}\" for x in dataset_dir.iterdir() if x.is_dir())\n",
    "testdata = MedNISTDataset(root_dir=root_dir, transform=transform, section=\"test\", download=False, runtime_cache=True)\n",
    "\n",
    "max_items_to_print = 10\n",
    "with eval_mode(model):\n",
    "    for item in DataLoader(testdata, batch_size=1, num_workers=0):\n",
    "        prob = np.array(model(item[\"image\"].to(\"cuda:0\")).detach().to(\"cpu\"))[0]\n",
    "        pred = class_names[prob.argmax()]\n",
    "        gt = item[\"class_name\"][0]\n",
    "        print(f\"Class prediction is {pred}. Ground-truth: {gt}\")\n",
    "        max_items_to_print -= 1\n",
    "        if max_items_to_print == 0:\n",
    "            break"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
